using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

namespace Voodoo.Sauce.Common.Font.Network
{
	public static class WebRequest
	{
		private static int Timeout = 5;

		private static Dictionary<int, IWebRequestHandler> idToResult = new Dictionary<int, IWebRequestHandler>();

		public static Header requestHeader;

		private static CancellationTokenSource _cancellationTokenSource;

		public static bool IsCancellationRequested
		{
			get
			{
				if (_cancellationTokenSource != null)
				{
					return _cancellationTokenSource.IsCancellationRequested;
				}
				return false;
			}
		}

		public static void CancelAllTasks()
		{
			_cancellationTokenSource.Cancel();
		}

		public static void Get(string url, Action<UnityWebRequest> onSuccess = null, Action<UnityWebRequest> onError = null)
		{
			SendAndCache(UnityWebRequest.Get(url), onSuccess, onError);
		}

		public static async Task<UnityWebRequest> GetAsync(string url)
		{
			UnityWebRequest request = UnityWebRequest.Get(url);
			await SendAndCacheAsync(request);
			return request;
		}

		public static void Put(string url, string content, Action<UnityWebRequest> onSuccess = null, Action<UnityWebRequest> onError = null)
		{
			UnityWebRequest unityWebRequest = UnityWebRequest.Put(url, content);
			unityWebRequest.uploadHandler.contentType = "application/json";
			SendAndCache(unityWebRequest, onSuccess, onError);
		}

		public static async Task<UnityWebRequest> PutAsync(string url, string content)
		{
			UnityWebRequest request = UnityWebRequest.Put(url, content);
			request.uploadHandler.contentType = "application/json";
			await SendAndCacheAsync(request);
			return request;
		}

		public static void Post(string url, string content, Action<UnityWebRequest> onSuccess = null, Action<UnityWebRequest> onError = null)
		{
			SendAndCache(new UnityWebRequest(url, "POST", new DownloadHandlerBuffer(), new UploadHandlerRaw(Encoding.UTF8.GetBytes(content)))
			{
				uploadHandler = 
				{
					contentType = "application/json"
				}
			}, onSuccess, onError);
		}

		public static async Task<UnityWebRequest> PostAsync(string url, string content)
		{
			UnityWebRequest request = new UnityWebRequest(url, "POST", new DownloadHandlerBuffer(), new UploadHandlerRaw(Encoding.UTF8.GetBytes(content)))
			{
				uploadHandler = 
				{
					contentType = "application/json"
				}
			};
			await SendAndCacheAsync(request);
			return request;
		}

		private static void SendAndCache(UnityWebRequest request, Action<UnityWebRequest> onSuccess = null, Action<UnityWebRequest> onError = null)
		{
			ApplyHeader(request);
			UnityWebRequestAsyncOperation unityWebRequestAsyncOperation = request.SendWebRequest();
			if (unityWebRequestAsyncOperation != null)
			{
				idToResult.Add(unityWebRequestAsyncOperation.GetHashCode(), new WebRequestHandler(onSuccess, onError));
				unityWebRequestAsyncOperation.completed += OnAsyncOperationComplete;
			}
		}

		private static async Task SendAndCacheAsync(UnityWebRequest request)
		{
			if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
			{
				_cancellationTokenSource = new CancellationTokenSource();
			}
			ApplyHeader(request);
			UnityWebRequestAsyncOperation asyncOperation = request.SendWebRequest();
			if (asyncOperation != null)
			{
				while (!asyncOperation.isDone && !_cancellationTokenSource.IsCancellationRequested)
				{
					await Task.Yield();
				}
				if (_cancellationTokenSource.IsCancellationRequested)
				{
					Debug.Log("asyncOperation got canceled " + request.url);
					_cancellationTokenSource.Token.ThrowIfCancellationRequested();
				}
			}
		}

		private static void ApplyHeader(UnityWebRequest request)
		{
			if (requestHeader?.value == null || requestHeader.value.Length <= 0)
			{
				return;
			}
			for (int i = 0; i < requestHeader.value.Length; i++)
			{
				char c = requestHeader.value[i];
				if (!char.IsLetterOrDigit(c) && c != ' ' && c != '-')
				{
					return;
				}
			}
			request.SetRequestHeader(requestHeader.name, requestHeader.value);
			request.timeout = Timeout;
		}

		private static void OnAsyncOperationComplete(AsyncOperation operation)
		{
			if (!(operation is UnityWebRequestAsyncOperation unityWebRequestAsyncOperation))
			{
				return;
			}
			IWebRequestHandler webRequestHandler = (idToResult.ContainsKey(unityWebRequestAsyncOperation.GetHashCode()) ? idToResult[unityWebRequestAsyncOperation.GetHashCode()] : null);
			if (webRequestHandler != null)
			{
				if (string.IsNullOrEmpty(unityWebRequestAsyncOperation.webRequest.error))
				{
					webRequestHandler.OnSuccess(unityWebRequestAsyncOperation.webRequest);
				}
				else
				{
					webRequestHandler.OnError(unityWebRequestAsyncOperation.webRequest);
				}
				unityWebRequestAsyncOperation.webRequest.Dispose();
				idToResult.Remove(operation.GetHashCode());
			}
		}

		public static bool HadErrors(Task<UnityWebRequest> request)
		{
			return !string.IsNullOrEmpty(request.Result.error);
		}

		public static bool HadErrors(UnityWebRequest request)
		{
			return !string.IsNullOrEmpty(request.error);
		}
	}
}
